#include "log.h"
#include "page.h"
#include "dict_manager.h"
//#include "db_operate.h"
//#include "db_manager.h"
/** 
 * @copyright		all rights reserved.
 * @author			how_fly@yahoo.com
 * @version			1.0
 * @date
 * 
 * 
 */

#include "schema_manager.h"
#include "keyword.h"

#include <iostream>
#include <fstream>
using namespace std;

#define DICT_FILENAME "dict.txt"

dict_manager_t g_dict_manager;

/**
 *  @brief 	1.after search the word in this page, this page will push
 * 			  to the schema, which will store the result
 * 			2.there are multi-threads searching word in this page, 
 * 			  so each page has its own reference-count. the function
 *            'add_page_to_dict_manager' increases the reference-count,
 * 			  and the 'pushto_schema' decreases the reference-count. when
 * 			  'reference-count' is equal to zero,  the page will push to
 * 			  schema, and the page will be release.
 			
 *  @param [in]page_t 
 *  @return void
 */ 
void pushto_schema(page_t* page)
{
	///when page reference-count is 0, push it to db_manager,
	///and release the page
	if(release_page(page, false) == 0)
	{
		print_keyword(page->keyword); ///only for show in console
		add_page_to_schema(page);	///push page to schema
		release_page(page, true);  ///release the  page
	}	
}

/**
 *  @brief 	release all pages, dictionary, pthread_mutex, and pthread_cond
 *  @param [in]list<page_t*>& pages: 
 *  @param [in]set<dict_t>& dict
 *  @param [in]pthread_mutex_t& locker
 *  @param [in]pthread_cond_t&  cond 
 *  @return void
 */ 
void quit_pthread_parse_page( list<page_t*>& pages, set<dict_t>& dict,
			pthread_mutex_t& locker, pthread_cond_t&  cond)
{
	list<page_t*>::iterator it = pages.begin();
	for( ; it != pages.end(); it++)
	{
		release_page((*it),true); 
	} 
	pages.clear();
	
	
	dict.clear();
	
	pthread_mutex_destroy(&locker);
	pthread_cond_destroy(&cond);
}

/**
 *  @brief 	decide whether it is match the whole word.
 *  @param [in]char* word_end 
 *  @return bool: 'true' is in matched.
 */ 
bool is_independent_word(char* word_end)
{
	///eg:
	///when 'ab' in about, the 'next' character is 'o', so it is not matched 
	///the whole word. in other conditions, such as 'ab,' the last char
	/// is not character, so, it is matched.
	return isalpha((int)(char)(*word_end)) == 0;

}

/**
 *  @brief will search word from page in cycle
 *  @param [in]void* data: the index of threads.
 *  @return (void*)0
 */ 
void* pthread_parse_page(void* data)
{
	///detch the thread from main-thread, 
	///so the main-thread will not wait for each thread
	pthread_detach(pthread_self());	
	
	///get the index of threads
	///import: if( data == 0) , 0 is one of dict index
	int i = (int)data;
	set<dict_t>& dict = g_dict_manager.dicts[i];
	set<dict_t>::iterator it;
	list<page_t*>& pages = g_dict_manager.pages[i];
	pthread_mutex_t& locker = g_dict_manager.lockers[i];;
	pthread_cond_t&  cond = g_dict_manager.conds[i];

	///only quite == true, can jump out this cycle
	while(1)
	{
		///wait for new page by thread_mutex and thread_cond
		pthread_mutex_lock(&locker);
		while(pages.size()==0)
		{
			pthread_cond_wait(&cond, &locker);
		}
		page_t* page = pages.front();
		pages.pop_front();
		pthread_mutex_unlock(&locker);

		///get the 'body' form page, which not include the 'html-tag' content
		char* body = page->body.p;
		if( body != 0)
		{
			char* tmp = body;
			const char* dict_word = 0;
			int word_len = 0;
			keyword_t* keyword = page->keyword; ///which will store the search result
			for(it = dict.begin(); it != dict.end(); it++)
			{  
				dict_word = (*it).p;  ///the word to be retrieved
				word_len = strlen(dict_word);
				tmp = body;
				///search the whole body, get the whole numbers of this word in this page
				while((tmp = strstr(tmp, dict_word)) != 0) ///using the simple strstr, in future will use more quick search algorithm
				{
					///is whole matched
					if(is_independent_word(tmp+word_len))
					{
						///store the found word, and increase the count
						add_keyword(keyword,dict_word, 1);
					}
					tmp += word_len;
				}
			}
		}
		///finish search this page in this thread,
		///more detail, see annotation of this function
		pushto_schema(page);
		
		///if quit is true, will jump out of the cycle, 
		///when no more page is wating
		if(g_dict_manager.quit)
		{
			pthread_mutex_lock(&locker);
			if(pages.size()==0) ///only no more page, can jump out
			{
				pthread_mutex_unlock(&locker);
				break;
			}
			pthread_mutex_unlock(&locker);
		}
	}
	
	///will release this params
	quit_pthread_parse_page( pages, dict,locker, cond);
	return (void*)0;
}

/**
 *  @brief 	1.load the dictionary,
 * 			2.start the multi-threads. each thread will search words in web-pages 
 * 			  in one cycle.
 *  @param [in]dict_manager_t 
 *  @return int: 0 is good, -1 is error.
 */ 
int init_dict_manager(dict_manager_t& dict_manager)
{
	dict_manager.quit = false;
	
	///read the dict file
	ifstream inputFile(DICT_FILENAME);
	if( inputFile == 0)
		return -1;

	///insert word in the file to the dict
	int i = 0;
	string p;
	while(getline(inputFile, p))
	{
		p.erase(p.find_last_not_of('\r')+1);///delete '\r' in the end
		if( p.length() < DICT_WORD_LEN)
		{
			dict_t dict;
			memset(dict.p, 0, sizeof(dict.p));
			memcpy(dict.p, p.c_str(), p.length());
			dict_manager.dicts[i % DICT_THREADS_COUNT].insert(dict);
			i++;
		}
	}	
	
	inputFile.close();
	
	///initialize the thread_mutex and thread_cond
	for(int i = 0; i < DICT_THREADS_COUNT; i++)
	{
		pthread_mutex_init(&dict_manager.lockers[i], NULL);
	}
	for(int i = 0; i < DICT_THREADS_COUNT; i++)
	{
		pthread_cond_init(&dict_manager.conds[i], NULL);
	}
	
	///start the multi-threads
	pthread_t th_dict_manager[DICT_THREADS_COUNT];
	for(int i = 0 ; i < DICT_THREADS_COUNT; i++)
	{
		pthread_create(&th_dict_manager[i], NULL, &pthread_parse_page,
					   (void*)i);
	}
	return 0;
	
}

/**
 *  @brief 	add one web-page to all threads, which will search word in
 * 			each cycle.
 *  @param [in]page_t	pointer to the page 
 *  @param [in]dict_manager_t
 *  @return int: 0 is good, -1 is error.
 */
int add_page_to_dict_manager(page_t* page, dict_manager_t* dict_manager)
{
	if( page == 0 || dict_manager == 0)
		return -1;
	
			
	printf("dict_manager: for %s\n",  page->url.szurl);
	logger("dict_manager: for %s\n", page->url.szurl);
	
	///increas the reference-count in page, see 'pushto_schema' annotation
	increase_page(page,DICT_THREADS_COUNT);	
	for(int choice = 0 ; choice < DICT_THREADS_COUNT; choice++)
	{
		pthread_mutex_lock(&dict_manager->lockers[choice]);
		dict_manager->pages[choice].push_back(page);
		pthread_cond_signal(&dict_manager->conds[choice]);
		pthread_mutex_unlock(&dict_manager->lockers[choice]);
	}	
	return 0;
}
/**
 *  @brief 	dict_manager_t will jump out of the cycle in each thread
 *  @param [in]dict_manager_t 
 *  @return void
 */
void quit_dict_manager(dict_manager_t* dict_manager)
{
	dict_manager->quit = true;
}
